﻿using System.Runtime.CompilerServices;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using DimensionsHelper.Client.Converters;
using DimensionsHelper.Client.ViewModels.CsvFormatter;

namespace DimensionsHelper.Client.Helpers;

public class TextBindHelper
{

    private static readonly FontSizePtConverter FontSizePtConverter = new();


    public static readonly DependencyProperty BindRangeStyleProperty =
        DependencyProperty.RegisterAttached(
            "BindRangeStyle",
            typeof(IRangeStyleOptions),
            typeof(TextBindHelper),
            new PropertyMetadata(null, OnBindRangeStylePropertyChanged));

    
    public static readonly DependencyProperty BindFontStyleProperty =
        DependencyProperty.RegisterAttached(
            "BindFontStyle",
            typeof(IFontStyleOptions),
            typeof(TextBindHelper),
            new PropertyMetadata(null, OnBindFontStylePropertyChanged));
    

    public static readonly DependencyProperty HideRunProperty =
        DependencyProperty.RegisterAttached(
            "HideRun",
            typeof(bool),
            typeof(TextBindHelper),
            new PropertyMetadata(false, OnHideRunPropertyChanged));


    public static readonly DependencyProperty ShowRunProperty =
        DependencyProperty.RegisterAttached(
            "ShowRun",
            typeof(bool),
            typeof(TextBindHelper),
            new PropertyMetadata(true, OnShowRunPropertyChanged));


    private static readonly ConditionalWeakTable<Run, RunValueInfo> RunInfoStorage = new();


    public static string? GetBindRangeStyle(DependencyObject obj)
    {
        return obj.GetValue(BindRangeStyleProperty) as string;
    }


    public static void SetBindRangeStyle(DependencyObject d, string value)
    {
        d.SetValue(BindRangeStyleProperty, value);
    }


    private static readonly string FontFamilyPath = $"{nameof(IRangeStyleOptions.FontFamily)}.{nameof(FontFamilyInfo.Family)}";

    private static void OnBindRangeStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // FontFamily
        BindingOperations.SetBinding(d, TextBlock.FontFamilyProperty, 
            new Binding(FontFamilyPath) 
            { 
                Source = e.NewValue 
            });

        // FontSize
        BindingOperations.SetBinding(d, TextBlock.FontSizeProperty, 
            new Binding(nameof(IRangeStyleOptions.FontSize))
            {
                Source = e.NewValue,
                Converter = FontSizePtConverter
            });

        // Bold
        BindingOperations.SetBinding(d, TextBlock.FontWeightProperty, 
            new Binding(nameof(IRangeStyleOptions.Bold))
            {
                Source = e.NewValue,
                Converter = BoldToFontWeightConverter.Instance
            });

        // Italic
        BindingOperations.SetBinding(d, TextBlock.FontStyleProperty, 
            new Binding(nameof(IRangeStyleOptions.Italic))
            {
                Source = e.NewValue,
                Converter = ItalicToFontStyleConverter.Instance
            });

        // Foreground
        BindingOperations.SetBinding(d, TextBlock.ForegroundProperty, 
            new Binding(nameof(IRangeStyleOptions.FontColorBrush)) 
            { 
                Source = e.NewValue 
            });

        // HorizontalAlignment
        BindingOperations.SetBinding(d, FrameworkElement.HorizontalAlignmentProperty,
            new Binding(nameof(IRangeStyleOptions.HAlign))
            {
                Source = e.NewValue,
                Converter = AlignConverter.Instance
            });

        // VerticalAlignment
        BindingOperations.SetBinding(d, FrameworkElement.VerticalAlignmentProperty,
            new Binding(nameof(IRangeStyleOptions.VAlign))
            {
                Source = e.NewValue,
                Converter = AlignConverter.Instance
            });

        // TextDecorations
        BindingOperations.SetBinding(d, TextBlock.TextDecorationsProperty, new MultiBinding
        {
            Converter = TextDecorationConverter.Instance,
            Bindings =
            {
                new Binding(nameof(IRangeStyleOptions.UnderLineType)) 
                { 
                    Source = e.NewValue 
                },
                new Binding(nameof(IRangeStyleOptions.Underlined)) 
                { 
                    Source = e.NewValue 
                }
            }
        });

        // WrapText
        BindingOperations.SetBinding(d, TextBlock.TextWrappingProperty, 
            new Binding(nameof(IRangeStyleOptions.WrapText))
            {
                Source = e.NewValue,
                Converter = BooleanToTextWrappingConverter.Instance
            });
    }

    
    public static string? GetBindFontStyle(DependencyObject obj)
    {
        return obj.GetValue(BindFontStyleProperty) as string;
    }

    public static void SetBindFontStyle(DependencyObject obj, string? value)
    {
        obj.SetValue(BindFontStyleProperty, value);
    }


    private static void OnBindFontStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // FontFamily
        BindingOperations.SetBinding(d, TextElement.FontFamilyProperty, 
            new Binding(FontFamilyPath)
            { 
                Source = e.NewValue 
            });

        // FontSize
        BindingOperations.SetBinding(d, TextElement.FontSizeProperty, 
            new Binding(nameof(IFontStyleOptions.FontSize)) 
            { 
                Source = e.NewValue,
                Converter = FontSizePtConverter
            });

        // FontWeight
        BindingOperations.SetBinding(d, TextElement.FontWeightProperty, 
            new Binding(nameof(IFontStyleOptions.Bold))
            {
                Source = e.NewValue,
                Converter = BoldToFontWeightConverter.Instance
            });

        // FontStyle
        BindingOperations.SetBinding(d, TextElement.FontStyleProperty, 
            new Binding(nameof(IFontStyleOptions.Italic))
            {
                Source = e.NewValue,
                Converter = ItalicToFontStyleConverter.Instance
            });

        // Foreground
        BindingOperations.SetBinding(d, TextElement.ForegroundProperty, 
            new Binding(nameof(IFontStyleOptions.FontColorBrush))
            {
                Source = e.NewValue
            });

        // TextDecorations
        if (d is Run run)
        {
            run.SetBinding(Inline.TextDecorationsProperty, new MultiBinding
            {
                Converter = TextDecorationConverter.Instance,
                Bindings =
                {
                    new Binding(nameof(IFontStyleOptions.UnderLineType)) { Source = e.NewValue },
                    new Binding(nameof(IFontStyleOptions.Underlined)) { Source = e.NewValue }
                }
            });
        }

        if (d is TextBlock textBlock)
        {
            textBlock.SetBinding(TextBlock.TextDecorationsProperty, new MultiBinding
            {
                Converter = TextDecorationConverter.Instance,
                Bindings =
                {
                    new Binding(nameof(IFontStyleOptions.UnderLineType)) { Source = e.NewValue },
                    new Binding(nameof(IFontStyleOptions.Underlined)) { Source = e.NewValue }
                }
            });
        }
    }

    public static bool GetHideRun(DependencyObject d)
    {
        return (bool)d.GetValue(HideRunProperty);
    }

    public static void SetHideRun(DependencyObject d, bool value)
    {
        d.SetValue(HideRunProperty, value);
    }

    private static void OnHideRunPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not Run run)
        {
            return;
        }

        if (!RunInfoStorage.TryGetValue(run, out RunValueInfo? info))
        {
            Binding? bind = null;

            if (BindingOperations.IsDataBound(run, Run.TextProperty))
            {
                bind = BindingOperations.GetBinding(run, Run.TextProperty);
            }

            info = new RunValueInfo(run.Text, bind);
            RunInfoStorage.Add(run, info);
        }

        if ((bool)e.NewValue)
        {
            BindingOperations.ClearBinding(run, Run.TextProperty);
            run.SetCurrentValue(Run.TextProperty, "");
        }
        else
        {
            if (info.Binding != null)
            {
                BindingOperations.SetBinding(run, Run.TextProperty, info.Binding);
                run.GetValue(Run.TextProperty);
            }
            else
            {
                run.SetCurrentValue(Run.TextProperty, info.Text);
            }
        }
    }


    public static bool GetShowRun(DependencyObject d)
    {
        return (bool)d.GetValue(ShowRunProperty);
    }

    public static void SetShowRun(DependencyObject d, bool value)
    {
        d.SetValue(ShowRunProperty, value);
    }


    private static void OnShowRunPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not Run run)
        {
            return;
        }
        
        if (!RunInfoStorage.TryGetValue(run, out RunValueInfo? info))
        {
            Binding? bind = null;

            if (BindingOperations.IsDataBound(run, Run.TextProperty))
            {
                bind = BindingOperations.GetBinding(run, Run.TextProperty);
            }

            info = new RunValueInfo(run.Text, bind);
            RunInfoStorage.Add(run, info);
        }
        
        if ((bool)e.NewValue)
        {
            if (info.Binding != null)
            {
                BindingOperations.SetBinding(run, Run.TextProperty, info.Binding);
                run.GetValue(Run.TextProperty);
            }
            else
            {
                run.SetCurrentValue(Run.TextProperty, info.Text);
            }
        }
        else
        {
            BindingOperations.ClearBinding(run, Run.TextProperty);
            run.SetCurrentValue(Run.TextProperty, "");
        }
    }

    private record RunValueInfo(string Text, Binding? Binding);
}
